#include <sys/stat.h>
#include <signal.h>
#include "worker.h"
#include "submission.h"
#include "balancer.h"
#include <sys/wait.h>

string getCurrentDate() {
	char date[50];
	time_t rawtime;
	time(&rawtime);
	struct tm* timeinfo;
	
	timeinfo = localtime(&rawtime);
	sprintf(date , "%d/%d/%d %02d:%02d:%02d" , timeinfo->tm_mday , timeinfo->tm_mon , timeinfo->tm_year + 1900 , timeinfo->tm_hour , timeinfo->tm_min , timeinfo->tm_sec);
	return string(date);
}

NewWorker::NewWorker(MessageChannel *pChannel , ofstream* pLogger , int pOutMsgType , pid_t pCommunicatorPid) {
	mWaitMutex = PTHREAD_MUTEX_INITIALIZER;
	mWaitCond = PTHREAD_COND_INITIALIZER;
	
	mLogger = pLogger;
	mChannel = pChannel;
	mOutMessageType = pOutMsgType;
	mCommunicatorPid = pCommunicatorPid;
	mBusy = false;
}

NewWorker::~NewWorker() {
	kill(mCommunicatorPid , SIGKILL);
}
	
void NewWorker::dispatch(string pSubmissionId) {
	mSubmissionId = pSubmissionId;
	mBusy = true;
	go();
}

bool NewWorker::isBusy() {
	return mBusy;
}

bool NewWorker::isCommunicatorAlive() {
	struct stat communicatorStat;
	char path[500];
	sprintf(path , "/proc/%d" , mCommunicatorPid);
	return (stat(path , &communicatorStat) == 0);
}

/*
///Pass to communicator/grader
		SubmissionMessage tMessage(mOutMessageType);
		tMessage.setSubmissionId(tCurrentSubmissionId.c_str());
		mChannel->send(&tMessage);
		
		if (mChannel->receive(&tMessage , tMessage.getLength()) < 0) { ///Channel error
			cout << "Error receiving message" << endl;
		}
		else if (tMessage.getSubmissionId()[0] != '0') { ///Grading error
			AppBalancer::getInstance()->registerFailedSubmission(tCurrentSubmissionId);
		}
		
		///Try to recover from failure, yet again
		recover(); 
*/

void NewWorker::recover() {
	if (!isCommunicatorAlive()) {
		*mLogger << getCurrentDate() << " Subs#" << mSubmissionId << " communicator failure (PID: " << mCommunicatorPid << "). Recovering" << endl;
		
		cout << getCurrentDate() << " Subs#" << mSubmissionId << " communicator failure (PID: " << mCommunicatorPid << "). Recovering" << endl;
		
		pid_t tPid = fork();
		if (tPid == 0) {		
			char sChannel[100], sMsgType[100];
				
			sprintf(sChannel , "%d" , mChannel->getId());
			sprintf(sMsgType , "%d" , mOutMessageType);
			
			execl(AppBalancer::getInstance()->getGraderPath().c_str() , "yiic" , "evaluate" , "queue" , sChannel , sMsgType , (char*) 0);
			exit(0);
		}
		else {
			mCommunicatorPid = tPid;
			
			*mLogger << getCurrentDate() << " Subs#" << mSubmissionId << " recovered (PID: " << mCommunicatorPid << ")" << endl;
			
			///Sleep for a while to give time for communicator startup. Oh my God it's so dirty
			///TODO: Fix it!
			sleep(2);
			
		}
	}
}

void NewWorker::grade() {
	string tCurrentSubmissionId = mSubmissionId;
	cout << "\tGrading submission #" << tCurrentSubmissionId << endl;
	*mLogger << getCurrentDate() << " Subs#" << tCurrentSubmissionId << " accepted by worker, start grading" << endl;
	
	pid_t pid = fork();
	if (pid == 0) {
		execl(AppBalancer::getInstance()->getGraderPath().c_str() , "yiic" , "evaluate" , tCurrentSubmissionId.c_str() , (char*) 0);
		exit(0);
	}
	else {
		int status;
		waitpid(pid , &status , 0);
	}
}
	
void NewWorker::execute() {
	cout << "Executing worker with communicator #" << mCommunicatorPid << " and alive : " << isCommunicatorAlive() << endl;
	
	while(1) {
		pthread_mutex_lock(&mWaitMutex);
		pthread_cond_wait(&mWaitCond , &mWaitMutex);
		
		grade();
		
		///Logging
	
		///Try to recover from failure
		//recover();
		
		///Pass to communicator/grader
		/*SubmissionMessage tMessage(mOutMessageType);
		tMessage.setSubmissionId(tCurrentSubmissionId.c_str());
		mChannel->send(&tMessage);
		
		if (mChannel->receive(&tMessage , tMessage.getLength()) < 0) { ///Channel error
			cout << "Error receiving message" << endl;
		}
		else if (tMessage.getSubmissionId()[0] != '0') { ///Grading error
			cout << "Grading error" << endl;
			AppBalancer::getInstance()->registerFailedSubmission(tCurrentSubmissionId);
		}*/
		
		///Try to recover from failure, yet again
		//recover(); 
		
		///Finishing
		cout << "\tDone grading submission #" << mSubmissionId << endl;
		*mLogger << getCurrentDate() << " Subs#" << mSubmissionId << " done" << endl;
		mBusy = false;
		
		pthread_mutex_unlock(&mWaitMutex);
	}
}
		
void NewWorker::go() {
	//usleep(1);
	//cout << "GO!" << endl;
	pthread_mutex_lock(&mWaitMutex);
	pthread_cond_signal(&mWaitCond);
	pthread_mutex_unlock(&mWaitMutex);
}
